package com.balabala.filter;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.balabala.constant.TokenConstant;
import com.balabala.dto.UserInfoDto;
import com.balabala.gym_enum.ExceptionEnum;
import com.balabala.result.Result;
import com.balabala.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author 何涛
 * @Date: 2023/04/24/ 18:36
 * @description 自定义登录认证的过滤器
 */
@Component
@Slf4j
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    @Autowired
    IgnoreWhiteListProperties ignoreWhiteListProperties;
    @Autowired
    RedisTemplate redisTemplate;
    private final static String ADMIN="admin";
    private final static String ADMIN_TOKEN="admin-token";
    private final static String USER_TOKEN="user-token";

    /**
     * 登录认证的方法
     * @param exchange
     * @param chain
     * @return
     */

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpResponse response = exchange.getResponse();
        List<String> tokens;
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest.Builder mutate = request.mutate();
        String uri = request.getURI().toString();
        log.info("用户：{}，正在访问uri：{}",request.getRemoteAddress().toString(),uri);
        if(ignoreWhiteListProperties.isIgnore(uri))
        {
            log.info("用户:{},正在访问白名单请求，不用拦截。。。",request.getRemoteAddress().toString());
            return chain.filter(exchange);
        }
        if(uri.contains(ADMIN)) {
            tokens = request.getHeaders().get(ADMIN_TOKEN);
        }
        else {
            tokens = request.getHeaders().get(USER_TOKEN);
        }
        log.info("用户正在访问需要认证的接口");
        //判断是否携带token
        if(tokens==null||tokens.isEmpty())
        {
            log.info("用户没有携带token。。。");
            return ResponseUtils.writeErrorInfo(response, Result.fail(ExceptionEnum.NOT_HAS_TOKEN));
        }
        String token = tokens.get(0);
        log.info("token:{}",token);
        //用户访问后台接口则使用jwt的方式判断
        if(uri.contains(ADMIN))
        {
            //验证token是否过期
            JWT jwt = JWTUtil.parseToken(token);
            Long expireTime = (Long) jwt.getPayload("expire_time");
            if(System.currentTimeMillis()>expireTime)
            {
                log.info("token已失效。。。");
                return ResponseUtils.writeErrorInfo(response,Result.fail(ExceptionEnum.TOKEN_EXPIRE));
            }
            //校验token是否合法
            if(!JwtUtils.checkToken(token))
            {
                log.info("token不合法。。。");
                return ResponseUtils.writeErrorInfo(response,Result.fail(ExceptionEnum.TOKEN_ILLEGAL));
            }
        }
        //用户访问前台接口则使用redis的方式判断
        else
        {
            UserInfoDto userInfo = (UserInfoDto) redisTemplate.boundValueOps(TokenConstant.USER_TOKEN_SECRET+token).get();
            if(userInfo==null)
            {
                log.info("用户token失效或不存在:{}",token);
                return ResponseUtils.writeErrorInfo(response,Result.fail(ExceptionEnum.NO_CACHE_DATA));
            }
            //刷新用户信息的失效时间
            redisTemplate.boundValueOps(TokenConstant.USER_TOKEN_SECRET+token).expire(TokenConstant.CACHE_USER_EXPIRE, TimeUnit.SECONDS);
        }


        //校验通过放行
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }
    /**
     * 全局过滤器的执行顺序，数值越低越先执行
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
